#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <math.h>
#include "dept150_chol_cpu_gpu_mkl.h"
#include "mkl_lapack.h"

int initCublasHandles(cublasHandle_t* handle, int gpu_n);

int destroyCublasHandles(cublasHandle_t* handle, int gpu_n);

int main(int argc, char** argv)
{
	double* A = NULL;
	int i, j;
	int N = 6;
	int block_size = 3;
	if(argc >=  2)
	{
		N = atoi(argv[1]);
		block_size = atoi(argv[2]);
	}	
	A = getA16(N);

	int n = N;
	int info = -10;

	gpu_based_matrix_t gpu_matrix;

	//checkCudaErrors(cudaGetDeviceCount(&(gpu_matrix.gpu_n)));
	gpu_matrix.gpu_n = 1;
	gpu_matrix.order = N;
	gpu_matrix.block_size = block_size;

	cublasHandle_t* handle = (cublasHandle_t *)malloc(gpu_matrix.gpu_n * sizeof(cublasHandle_t));
	
	if(initCublasHandles(handle, gpu_matrix.gpu_n))
	{
		printf("CUBLAS HANDLE INITIALIZATION FAILED\n");
		return EXIT_FAILURE;
	}
	
	if(copyMatrixToDevices(A, &gpu_matrix,handle ))
	{
		printf("INITIALIZATION OF DEVICE MATRIX FAILED");
		return EXIT_FAILURE;
	}

	clock_t start_time, end_time;
	start_time = clock();
	double *b;
	int res = factorize_one_gpu_based_matrix(gpu_matrix, handle);	
	end_time = clock();

	double *result_matrix = (double*)malloc(N*N*sizeof(double));
	copyMatrixFromDevices(result_matrix, gpu_matrix);

	//printf("Start MKL fact and check if matrix equlas to MKL result\n");
	//MKL_INT mkl_info;
	//dpotrf("L", &N, A,  &N, &mkl_info);

	//if(mkl_info)
	//{
	//	printf("MKL FACTORIZATION FAILED mkl_info - %d\n", mkl_info);
	//	return EXIT_FAILURE;
	//}

	//bool hasErrors = false;
	//for(int i = 0; i < N; i++)
	//{
	//	for(int j =0; j < N; j++)
	//	{
	//		if (fabs( result_matrix[IDF2(i, j, N)] - A[IDF2(i, j,N)] ) > 1e-6)
	//		{
	//			hasErrors = true;
	//			printf("For element [%i, %i] value of my %f value of mkl %f\n", i, j, result_matrix[IDF2(i, j, N)], A[IDF2(i, j,N)]); 
	//		}
	//	}
	//}

	//if(!hasErrors)
	//{
	//	printf("Comparing with MKL success\n");
	//}
	
	//printf("Matrix after factorize \n");
//	print_matrix(N, N, result_matrix, N);


	printf("\nTime : %.5f\n",(float)(end_time - start_time)/CLOCKS_PER_SEC);
	
	free(A);
	free(result_matrix);

	cublasStatus_t stat;
	for(int i = 0; i < gpu_matrix.gpu_n; i++)
	{
		checkCudaErrors(cudaSetDevice(i));
		checkCudaErrors(cudaFree(gpu_matrix.gpu_matrix_blocks[i]));
	}

	free(gpu_matrix.gpu_matrix_blocks);
}


int initCublasHandles(cublasHandle_t* handle, int gpu_n)
{
	cublasStatus_t stat;
	for(int i = 0; i < gpu_n; i++)
	{
		checkCudaErrors(cudaSetDevice(i));
		stat = cublasCreate(&handle[i]);
		if(stat != CUBLAS_STATUS_SUCCESS)
		{
			printf("CUBLAS INITIALIZATION FAILED");
			return EXIT_FAILURE;
		}
	}

	return EXIT_SUCCESS;
}

int destroyCublasHandles(cublasHandle_t* handle, int gpu_n)
{
	cublasStatus_t stat;
	for(int i = 0; i < gpu_n; i++)
	{
		checkCudaErrors(cudaSetDevice(i));
		stat = cublasDestroy(handle[i]);
		if(stat != CUBLAS_STATUS_SUCCESS)
		{
			printf("CUBLAS DESTROY FAILED");
			return EXIT_FAILURE;
		}
	}

	return EXIT_SUCCESS;
}


